﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Demo.BytesIO.TCP_Client
{
    public partial class Form1 : Form
    {
        // 连接tcp
        private TcpClient tcpClient =null;
        private NetworkStream stream = null;
        // 创建一个负责监听服务端请求的线程
        Thread threadClient = null;

        public const int TCPBufferSize = 1460; // 缓存的最大数据个数
        public byte[] TCPBuffer = new byte[TCPBufferSize]; // 缓存数据的数组
        public Form1()
        {
            InitializeComponent();
        }







        /*==================同步处理方法============*/

        /// <summary>
        /// 同步连接按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //private void tbConnect_Click(object sender, EventArgs e)
        //{
        //    if(string.IsNullOrEmpty(tbStrIp.Text) == false && string.IsNullOrEmpty(tbIntPort.Text) == false)
        //    {
        //        try
        //        {
        //            string strIp = tbStrIp.Text;
        //            int intPort = int.Parse(tbIntPort.Text);
                    
        //            if (tcpClient.Connected) // 如果已经连接了，点击连接，重新连接服务端
        //            {
        //                ReConnect(strIp, intPort);
        //                richTextBox1.AppendText("已经成功连接服务器\r\n");
        //            }
        //            else
        //            {
        //                // 连接服务端
        //                tcpClient.Connect(strIp, intPort);
        //                NetworkStream networkStream = tcpClient.GetStream(); // 获取服务端的网络流数据 

        //                richTextBox1.AppendText("成功连接服务器\r\n");
        //                if (ConnectStatus())
        //                {
        //                    stream = tcpClient.GetStream();
        //                }
        //                // 接收服务端的消息线程
        //                //创建一个线程 用于监听服务端发来的消息
        //                threadClient = new Thread(Recive);
        //                //将窗体线程设置为与后台同步
        //                threadClient.IsBackground = true;
        //                //启动线程
        //                threadClient.Start();
        //            }
        //        }
        //        catch (Exception ex)
        //        {
        //            MessageBox.Show(ex.ToString(), "提示");
        //        }
        //    }
        //    else
        //    {
        //        MessageBox.Show("请输入IP地址或端口号！", "提示");
        //    }
            
        //}
        /// <summary>
        /// 直接获取端口字符串数据没有则返回空
        /// </summary>
        private void Recive()
        {
            //定义一个1M的内存缓冲区 用于临时性存储接收到的信息
            byte[] data = new byte[1024];
            String responseData = String.Empty;
            while (true) // 持续监听服务端发来的消息
            {
                try
                {
                    int bytes = stream.Read(data, 0, data.Length);
                    responseData = Encoding.Default.GetString(data, 0, bytes);
                    if(responseData != null)
                    {
                        // 显示在日志上
                        // richTextBox1.AppendText(responseData); //会报错：控件不能控制线程，用线程控制线程
                        //用Action线程委托操作
                        Invoke((new Action(()=> {
                            richTextBox1.AppendText($"[{DateTime.Now}]：{responseData}\r\n");
                        })));
                        if (responseData == "")
                        {
                            Invoke((new Action(() => {
                                richTextBox1.AppendText($"[{DateTime.Now}]：服务端异常断开连接\r\n");
                            })));
                            stream.Close();
                            tcpClient.Close();
                            threadClient.Abort();
                            break;
                        }
                    }
                }
                catch (ObjectDisposedException odex)
                {
                    //服务端异常断开连接，然后点击断开出现的异常
                    // 结束循环
                    richTextBox1.AppendText("已断开连接\r\n");
                    break;
                }
            }
        }

        /// <summary>
        /// 断开服务端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tbDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                //先判断连接状态
                if (ConnectStatus()) // 存在连接服务端
                {
                    tcpClient.Close();
                    richTextBox1.AppendText("已断开连接\r\n");
                }
                else
                {
                    MessageBox.Show("未存在连接！！");
                }
            }
            catch
            {

            }
        }
        /// <summary>
        /// 清空接收按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClearConnect_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
        }
        /// <summary>
        /// 发送按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        //private void btnSend_Click(object sender, EventArgs e)
        //{
        //    // 获取输入框中的内容
        //    string initData = richTextBox2.Text;
        //    try
        //    {
        //        if(initData.Length > 0)
        //        {
        //            // 转换成字节
        //            byte[] data = Encoding.Default.GetBytes(initData);
        //            // 数据流写出
        //            stream.Write(data, 0, data.Length);
        //            // 在日志中显示
        //            richTextBox1.AppendText($"[{DateTime.Now}]：{initData}\r\n");

        //        }
        //    }
        //    catch (Exception)
        //    {

        //    }

        //}
        /// <summary>
        /// 清空发送按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClearSend_Click(object sender, EventArgs e)
        {
            // 清空输入框里的内容
            richTextBox2.Clear();
        }
        
        /// <summary>
        /// 连接的状态
        /// </summary>
        /// <returns></returns>
        private bool ConnectStatus()
        {
            try
            {
                return tcpClient.Connected;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 服务端状态
        /// </summary>
        /// <returns></returns>
        private bool ServerStatus()
        {
            return false;
        }
        /// <summary>
        /// 重新连接服务端
        /// </summary>
        /// <param name="strIp"></param>
        /// <param name="intPort"></param>
        private void ReConnect(string strIp, int intPort)
        {
            try
            {
                if (tcpClient != null)
                {
                    tcpClient.Close();
                }
                tcpClient = new TcpClient(strIp, intPort);
                if (tcpClient.Connected)
                {
                    stream = tcpClient.GetStream();
                }
            }
            catch (Exception ex)
            {

            }
        }






        /*==========================异步处理方法====================*/

        /// <summary>
        /// 连接按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tbConnect_Click(object sender, EventArgs e)
        {
            // 判断IP地址和端口号不能为空
            if(string.IsNullOrEmpty(tbStrIp.Text) == false && string.IsNullOrEmpty(tbIntPort.Text) == false)
            {
                try
                {
                    // 获取IP地址和端口号
                    string strIP = tbStrIp.Text;
                    int intPort = int.Parse(tbIntPort.Text);

                    // 创建一个tcpClient
                    tcpClient = new TcpClient();
                    // 根据服务端的IP地址和端口号 异步连接服务器
                    tcpClient.BeginConnect(strIP, intPort, new AsyncCallback(ConnectCallback), tcpClient);
                }
                catch (Exception)
                {

                }
            }
            else if(string.IsNullOrEmpty(tbIntPort.Text))
            {
                MessageBox.Show("请输入IP地址", "提示");
            }
            else if (string.IsNullOrEmpty(tbIntPort.Text))
            {
                MessageBox.Show("请输入端口", "提示");
            }
        }

        /// <summary>
        /// 连接异步回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void ConnectCallback(IAsyncResult ar)
        {
            TcpClient tcp = (TcpClient)ar.AsyncState;
            // System.InvalidOperationException:“不允许对非连接的套接字执行此操作。”
            //stream = tcp.GetStream();
            stream = tcpClient.GetStream();//创建于服务端连接的数据流
            try
            {
                tcp.EndConnect(ar);
                // 设置异步读取数据，接收的数据缓存到TCPBuffer，接收完成跳转ReadCallback函数
                stream.BeginRead(TCPBuffer, 0, TCPBufferSize, new AsyncCallback(ReadCallback), stream);
                Invoke((new Action(()=> {
                    richTextBox1.AppendText($"[{DateTime.Now}] 服务端连接成功！\r\n");
                })));
            }catch(Exception ex)
            {
                Invoke((new Action(()=> {
                    richTextBox1.AppendText($"[{DateTime.Now}] 连接失败：{ex.ToString()}\r\n");
                })));
            }
        }
        /// <summary>
        /// 接收服务端数据的回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void ReadCallback(IAsyncResult ar)
        {
            /*无法将类型为“System.Net.Sockets.NetworkStream”的对象强制转换为类型“System.Net.Sockets.TcpClient”。”
            *TcpClient tcp = (TcpClient)ar.AsyncState;  
            *int CanReadLen = tcp.Client.EndReceive(ar);
            * 用这种方式需要把stream.BeginRead(TCPBuffer, 0, TCPBufferSize, new AsyncCallback(ReadCallback), stream);
            * 最后面的一个参数改为TcpClient类型，否则会报最上面的类型无法转化的错误
            */
            NetworkStream networkStream = (NetworkStream)ar.AsyncState;
            int CanReadLen = networkStream.EndRead(ar);
            if (CanReadLen > 0)
            {
                string data = Encoding.Default.GetString(TCPBuffer, 0, CanReadLen);
                Invoke((new Action(() => {
                    richTextBox1.AppendText($"[{DateTime.Now}]：{data}\r\n");
                })));
                // 设置异步读取数据，接收的数据缓存到TCPBuffer，接收完成跳转ReadCallback函数
                stream.BeginRead(TCPBuffer, 0, TCPBufferSize, new AsyncCallback(ReadCallback), stream);
            }
            else
            {   //异常
                Invoke((new Action(() => {
                    richTextBox1.AppendText($"[{DateTime.Now}] 异常断开！\r\n");
                })));
                try
                {
                    tcpClient.Close();
                    stream.Close();
                }
                catch (Exception) { }
            }

        }

        /// <summary>
        /// 发送按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            //stream = tcpClient.GetStream(); // 创建于服务器连接的数据流
            // 获取文本框中的内容
            string rtbStr = richTextBox2.Text;
            try
            {
                if (rtbStr.Length > 0) //文本框里有内容
                {
                    // 转换成字节
                    byte[] data = Encoding.Default.GetBytes(rtbStr);
                    // 异步发送数据
                    //stream.BeginWrite(data, 0, data.Length, new AsyncCallback(SendCallback), stream);
                    // 发送数据
                    stream.Write(data, 0, data.Length);
                    Invoke((new Action(() => {
                        richTextBox1.AppendText($"[{DateTime.Now}]：{rtbStr}\r\n");
                    })));
                }
                else
                {
                    Invoke((new Action(() => {
                        richTextBox1.AppendText("请输入内容");
                    })));
                }
            }
            catch (Exception) { }
        }
    }
}
